#include <retro.h>
#include "retro.creator.h"
#include "retro.scriptingapi.h"

using namespace Retro::Scripting;
using namespace Retro::Creator;
using namespace Retro::Creator::API;
using namespace SPLib::Geom;

#define API_SetStyle(edit, style, color, size) \
  edit.send(SCI_STYLESETFORE, style, color); \
  edit.send(SCI_STYLESETSIZE, style, size); \
  edit.send(SCI_STYLESETFONT, style, (LPARAM)"Courier New");

#define LUA_KEYWORDS \
  "and break do else elseif end false for function if in local " ## \
  "nil not or repeat return then true until while";

#define Editor_IsMap(editor) \
  (wEditor->getResource()->getType() == Data::ResourceGridMap || \
  wEditor->getResource()->getType() == Data::ResourceSpriteMap)

#define Editor_IsGridMap(editor) \
  wEditor->getResource()->getType() == Data::ResourceGridMap

#define Editor_IsSpriteMap(editor) \
  wEditor->getResource()->getType() == Data::ResourceSpriteMap

template <typename T>
VOID GetEditor(T*& oEditor, INT iType)
{
  oEditor = NULL;

  if (!Frame::get()->getEditor())
    return;

  if (!((INT)Frame::get()->getEditor()->getResource()->getType() & iType))
    return;

  oEditor = (T*)Frame::get()->getEditor();
}

template <typename T>
VOID GetEditor(T*& oEditor, INT iType1, INT iType2)
{
  oEditor = NULL;

  if (!Frame::get()->getEditor())
    return;

  if (((INT)Frame::get()->getEditor()->getResource()->getType()
    & iType1) != iType1 &&
    ((INT)Frame::get()->getEditor()->getResource()->getType()
    & iType2) != iType2)
    return;

  oEditor = (T*)Frame::get()->getEditor();
}

VOID Creator::API::InitializeEditor(
  HWND iEditor,
  Data::ScriptLanguage iLanguage
  )
{
  WrapWnd mEditor(iEditor);
  LPARAM wKeywords = NULL;

  switch (iLanguage)
  {
  case Data::ScriptLua:
    wKeywords = (LPARAM)LUA_KEYWORDS;
    break;
  }

  mEditor.send(SCI_SETLEXER,          SCLEX_LUA,          0);
  mEditor.send(SCI_SETKEYWORDS,       0,                  wKeywords);
  mEditor.send(SCI_SETTABWIDTH,       2,                  0);
  mEditor.send(SCI_SETTABINDENTS,     TRUE,               0);
  mEditor.send(SCI_SETMARGINTYPEN,    SCRIPT_MARGIN_LINE, SC_MARGIN_NUMBER);
  mEditor.send(SCI_SETMARGINWIDTHN,   SCRIPT_MARGIN_LINE, 30);
  mEditor.send(SCI_SETSCROLLWIDTH,    200,                0);
  mEditor.send(SCI_SETVIEWWS,         SCWS_VISIBLEALWAYS, 0);
  mEditor.send(SCI_SETWHITESPACEFORE, TRUE,               0xBBBBBB);

  mEditor.send(SCI_SETCARETLINEVISIBLE,   TRUE,      0);
  mEditor.send(SCI_SETCARETLINEBACK,      0x000000,  0);
  mEditor.send(SCI_SETCARETLINEBACKALPHA, 0x22,      0);

  mEditor.send(SCI_SETMARGINTYPEN,  SCRIPT_MARGIN_FOLD, SC_MARGIN_SYMBOL);
  mEditor.send(SCI_SETMARGINMASKN,  SCRIPT_MARGIN_FOLD, SC_MASK_FOLDERS);
  mEditor.send(SCI_SETMARGINWIDTHN, SCRIPT_MARGIN_FOLD, 16);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDER,        SC_MARK_BOXPLUS);
  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDER,        0x999999);
  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDER,        0xFFFFFF);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDER,        0x999999);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDEROPEN,    SC_MARK_BOXMINUS);

  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDEROPEN,    0xFFFFFF);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDEROPEN,    0x999999);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDEREND,     SC_MARK_BOXPLUSCONNECTED);
  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDEREND,     0xFFFFFF);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDEREND,     0x999999);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERMIDTAIL, SC_MARK_TCORNER);
  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDERMIDTAIL, 0xFFFFFF);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERMIDTAIL, 0x999999);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDEROPENMID, SC_MARK_BOXMINUSCONNECTED);
  mEditor.send(SCI_MARKERSETFORE, SC_MARKNUM_FOLDEROPENMID, 0xFFFFFF);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDEROPENMID, 0x999999);

  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERSUB,     SC_MARK_VLINE);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERSUB,     0x999999);
  mEditor.send(SCI_MARKERDEFINE,  SC_MARKNUM_FOLDERTAIL,    SC_MARK_LCORNER);
  mEditor.send(SCI_MARKERSETBACK, SC_MARKNUM_FOLDERTAIL,    0x999999);

  mEditor.send(SCI_SETPROPERTY,   (WPARAM)"fold",           (LPARAM)"1");
  mEditor.send(SCI_SETPROPERTY,   (WPARAM)"fold.compact",   (LPARAM)"0");
  mEditor.send(SCI_SETPROPERTY,   (WPARAM)"fold.comment",   (LPARAM)"1");

  mEditor.send(SCI_SETMARGINSENSITIVEN, SCRIPT_MARGIN_FOLD, TRUE);

  API_SetStyle(mEditor, STYLE_DEFAULT,         0x000000, 10);
  mEditor.send(SCI_STYLECLEARALL, 0, 0);

  API_SetStyle(mEditor, SCE_LUA_DEFAULT,       0x000000, 10);
  API_SetStyle(mEditor, SCE_LUA_COMMENT,       0x666666, 10);
  API_SetStyle(mEditor, SCE_LUA_COMMENTLINE,   0x666666, 10);
  API_SetStyle(mEditor, SCE_LUA_COMMENTDOC,    0x666666, 10);
  API_SetStyle(mEditor, SCE_LUA_NUMBER,        0x0000FF, 10);
  API_SetStyle(mEditor, SCE_LUA_WORD,          0xFF0000, 10);
  API_SetStyle(mEditor, SCE_LUA_STRING,        0x0000FF, 10);
  API_SetStyle(mEditor, SCE_LUA_CHARACTER,     0x000000, 10);
  API_SetStyle(mEditor, SCE_LUA_LITERALSTRING, 0x666666, 10);
  API_SetStyle(mEditor, SCE_LUA_PREPROCESSOR,  0xFF0000, 10);
  API_SetStyle(mEditor, SCE_LUA_OPERATOR,      0x000000, 10);
  API_SetStyle(mEditor, SCE_LUA_IDENTIFIER,    0x000000, 10);
  API_SetStyle(mEditor, SCE_LUA_STRINGEOL,     0x000000, 10);
}

API_DeclareFunction(Exit)
{
  Frame::get()->doAction(FrameExit);
}

API_DeclareFunction(About)
{
  Frame::get()->doAction(FrameShowAbout);
}

API_DeclareFunction(PrintLine)
{
  Frame::get()->setStatus(FrameScriptor, iScript->getString(1));
  Frame::get()->getScriptor()->skipStatusChange();
}

API_DeclareFunction(EditResource)
{
  Data::RetroProject& wProject = Frame::get()->getProject();

  Frame::get()->editResource(wProject.findResource(
    (Data::ResourceType)iScript->getInt(2), iScript->getString(1)));
}

API_DeclareFunction(IsWireframe)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  iScript->pushBool(wMap->getDevice()->isWireframe());
}

API_DeclareFunction(SetWireframe)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  wMap->getDevice()->setWireframe(iScript->getBool(1));
  wMap->invalidate(FALSE);
}

API_DeclareFunction(RotateView)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  wMap->getDevice()->transform(
    Vector::Vector2D(0.0f, 0.0f),
    Vector::Vector2D(1.0f, 1.0f),
    Vector::Vector2D(
    (REAL)wMap->getDevice()->getWidth() / 2,
    (REAL)wMap->getDevice()->getHeight() / 2),
    RADIAN(iScript->getFloat(1)),
    Vector::Vector2D(0.0f, 0.0f),
    Device::Display::MatrixView);

  wMap->invalidate(FALSE);
}

API_DeclareFunction(ResetView)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  wMap->getDevice()->resetTransform(Device::Display::MatrixView);
  wMap->invalidate(FALSE);
}

API_DeclareFunction(IsGridVisible)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  iScript->pushBool(wMap->getShowGrid());
}

API_DeclareFunction(AreIndicesVisible)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  iScript->pushBool(
    wGrid->getMap()->getType() == Data::ResourceGridMap ?
    wGrid->getShowIndices() : FALSE);
}

API_DeclareFunction(SetShowGrid)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  wMap->setShowGrid(iScript->getBool(1));
}

API_DeclareFunction(SetShowIndices)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  wGrid->setShowIndices(iScript->getBool(1));
}

API_DeclareFunction(GetGridTexture)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  iScript->push(wGrid->getGridTexture(
    iScript->getInt(1),
    iScript->getInt(2),
    iScript->getInt(3)));
}

API_DeclareFunction(SetGridTexture)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  wGrid->setGridTexture(
    iScript->getInt(1),
    iScript->getInt(2),
    iScript->getInt(3),
    iScript->getInt(4));
}

API_DeclareFunction(GetGridSelection)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  const Bounds &selection = wGrid->getGridSelection();

  iScript->push(selection.x);
  iScript->push(selection.y);
  iScript->push(selection.cx);
  iScript->push(selection.cy);
}

API_DeclareFunction(GetPixelSelection)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  Bounds selection = wGrid->getPixelSelection();

  iScript->push(selection.x);
  iScript->push(selection.y);
  iScript->push(selection.cx);
  iScript->push(selection.cy);
}

API_DeclareFunction(SetGridSelection)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  wGrid->setGridSelection(iScript->getBounds(1));
}

API_DeclareFunction(SetPixelSelection)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  wGrid->setPixelSelection(iScript->getBounds(1));
}

API_DeclareFunction(GetMapSize)
{
  Editor::MapEditor* wMap;
  GetEditor(wMap, Data::ResourceGridMap, Data::ResourceSpriteMap);

  if (!wMap) return;

  switch (Frame::get()->getResource()->getType())
  {
  case Data::ResourceGridMap:
  case Data::ResourceSpriteMap:
    iScript->push(wMap->getMap()->getWidth());
    iScript->push(wMap->getMap()->getHeight());
    break;
  default:
    iScript->push(0);
    iScript->push(0);
    break;
  }
}

API_DeclareFunction(GetTileSize)
{
  Editor::GridEditor* wGrid;
  GetEditor(wGrid, Data::ResourceGridMap);

  if (!wGrid) return;

  iScript->push(wGrid->getMapTileSize());
  iScript->push(wGrid->getMapTileSize());
}

API_DeclareFunction(GetTileSelection)
{
  const Bounds &selection = Frame::get()->getSelector()->getSelection();

  iScript->push(selection.x);
  iScript->push(selection.y);
  iScript->push(selection.cx);
  iScript->push(selection.cy);
}

API_DeclareFunction(SetTileSelection)
{
  Frame::get()->getSelector()->setSelection(iScript->getBounds(1));
}

API_DefineAPI(Creator::API::InitializeAPI)
{
  iScript->add(Data::ResourceAudio,     "AUDIO");
  iScript->add(Data::ResourceGridMap,   "GRIDMAP");
  iScript->add(Data::ResourceScript,    "SCRIPT");
  iScript->add(Data::ResourceSprite,    "SPRITE");
  iScript->add(Data::ResourceSpriteMap, "SPRITEMAP");
  iScript->add(Data::ResourceTexture,   "TEXTURE");

  API_AddFunction(Exit);
  API_AddFunction(About);

  API_AddFunction(PrintLine);

  API_AddFunction(EditResource);

  API_AddFunction(IsWireframe);
  API_AddFunction(SetWireframe);
  API_AddFunction(RotateView);
  API_AddFunction(ResetView);

  API_AddFunction(IsGridVisible);
  API_AddFunction(AreIndicesVisible);
  API_AddFunction(SetShowGrid);
  API_AddFunction(SetShowIndices);

  API_AddFunction(GetGridTexture);
  API_AddFunction(SetGridTexture);

  API_AddFunction(GetGridSelection);
  API_AddFunction(GetPixelSelection);

  API_AddFunction(SetGridSelection);
  API_AddFunction(SetPixelSelection);

  API_AddFunction(GetTileSelection);
  API_AddFunction(SetTileSelection);

  API_AddFunction(GetMapSize);
  API_AddFunction(GetTileSize);
}
